package utilities;

public class MathUtils
{
	public static int pow(
			final int x,
			final int pow
	) {
		if ( pow == 0 )
			return 1;
		int res = x;
		for ( int p = 1; p < pow; ++p )
			res *= x;
		return res;
	}
	
	/**
	 * reduct matrix to diagonal form
	 * NB! last row is the free vars column
	 * @param M
	 * @return
	 */
	public static double[][] reductToDiag(
			double[][] M
	) {
		final int h = M.length - 1;
		final int w = M[0].length;
		int i, col, row;
		double[] leadRow;
		double leadVal;
		
		int[] unprocessedRows = new int[h];
		for ( i = 0; i < h; ++i  )
			unprocessedRows[i] = i;
		int[] processedRows = new int[h];
		int processedRowNum = 0;
		
		for ( col = 0; col < w; ++col )
		{
			row = findNextLeadRow( M, col, unprocessedRows );
			if ( row == -1 )
				continue;
			
			leadRow = M[row];
			leadVal = updateLeadRow( leadRow, w, col );
			subLeadRow( col, row, h, row, M, leadRow, leadVal );
			
			processedRows[processedRowNum++] = row;
		}
		double[][] diagM = new double[h + 1][];
		int diagRowNum = 0;
		diagM[h] = M[h];
		for ( int _row : processedRows )
			diagM[diagRowNum++] = M[_row];
		
		return diagM;
	}
	
	/**
	 * substract current lead val from other rows with corresponding multiplicator
	 * NB! also updates free vars column
	 * @param leadCol
	 * @param M
	 * @param learRow
	 */
	private static void subLeadRow(
			final int leadCol,
			final int leadRow,
			final int h,
			final int w,
			double[][] M,
			final double[] leadRowArr,
			final double leadVal
	) {
		double[] freeVarsCol = M[h];
		freeVarsCol[leadRow] /= leadVal;
		double freeVarsLeadVal = freeVarsCol[leadRow];
		double mult;
		int r, c;
		for ( r = 0; r < h; ++r )
		{
			if ( r == leadRow )
				continue;
			double[] subRow = M[r];
			mult = subRow[leadCol] / leadVal;
			subRow[leadCol] = 0;
			for ( c = leadCol+1; c < w; ++c )
				subRow[c] -= leadRowArr[c] * mult;
			freeVarsCol[r] -= freeVarsLeadVal * mult;
		} // for r
	} // void subLeadRow
	/** 
	 * devide row on lead val
	 * NB! does not update free vars column
	 * @param leadRow
	 * @param rowLen
	 * @param col
	 * @return lead val
	 */
	private static double updateLeadRow(
			double[] leadRow,
			final int rowLen,
			int col
	) {
		double leadVal = leadRow[col];
		leadRow[col] = 1;
		for ( int i = col+1; i < rowLen; ++i )
			leadRow[i] /= leadVal;
		return leadVal;
	}
	private static int findNextLeadRow(
			final double[][] M,
			final int col,
			final int[] unprocessedRows
	) {
		for ( int row : unprocessedRows )
		{
			if ( row == -1 )
				continue;
			double val = M[row][col];
			if ( !isZero( val ) )
			{
				unprocessedRows[row] = -1;
				return row;
			}
				
		}
		
		return -1;
	} // int findNextLeadRow
	
	public static boolean isZero(
			double val
	) {
		if ( val > -eps && val < eps ) return true;
		else return false;
	}
	
	// consts
	public static double eps = 10E-15;
	// ~consts
}
